# -- coding: utf-8 --
"""
# MIAHX© SCRAT© 传感器标签编码器

**模块：MIAHX© / 多模态数据采集系统（SCRAT） / 数据采集器（Data Collector）**

Version: 0.4.10,
Last updated: October 4, 2024

传感器标签编码形如：`210702030100001`，表示 `2-10702-03-01-00-001`

## Contributor(s)
* YAO Naiming <[naiming@iscas.ac.cn](mailto:naiming@iscas.ac.cn)>

## Change log
### v0.4.9
- .19: [新增] ESensorDataFormat 新增 BMP
- .7: [注释] 新增枚举类注释，用于指导控制面板开发
### v0.4.7
- .17: [新增] 扩展 ESensorType，新增 VBNG_NAVIGATION
### v0.4.6
- .20: [新增] 扩展 ESensorType，新增 PVGC_SMART_EYE_SNAPSHOT，支持采集 SmartEye 处理软件中的可视化图像，用于 ICD 可视化
- .20: [新增] to_tuple 函数，同时调整与其相关的实现
- .18: [新增] 扩展 ESensorDataFormat，新增 H5, EDF 数据格式
- .11: [优化] 调整 SChannelType 和 SType 编码
### v0.4.5
- .25: [优化] 更新接口与实现，提供调用方的友好支持
- .24: [优化] 配合调用方实现，缩减 VBNG_* 的传感器种类
- .21: [优化] 为了支持 DDS IDL Zero-Copy 机制，将 STag 从 str 更新为整数
- .21: [新增] STag 解码器
- .15: [新增] 初始实现

---
Copyright © 2024-2025 MIAHX Contributors, ISCAS, and ASIC. All Rights Reserved.
Licensed under Apache-2.0.
"""

from enum import Enum


class ESensorDomain(Enum):
    # 未指定
    IGNORE = '0'
    # 用户
    HUMAN = '1'
    # 舱内
    CARRIER = '2'
    # 舱外
    ENVIRON = '3'


class ESensorType(Enum):
    # 未指定
    IGNORE = '00000'
    # 物理-视觉-彩色相机
    PVCC_UNSPECIFIED = '00100'
    # 物理-视觉-通用相机
    PVCC_GENERIC = '00101'
    # 物理-视觉-Basler相机
    PVCC_BASLER = '00102'
    # 物理-视觉-微软Kinect Azure相机
    PVCC_KINECT_AZURE = '00103'
    # 物理-视觉-英特尔RealSense相机
    PVCC_REAL_SENSE = '00104'
    # 物理-视觉-深度相机
    PVDC_DCAM_ = '00201'
    # 物理-视觉-奥比中光深度相机
    PVDC_ORBIC_ = '00231'
    # 物理-视觉-LeapMotion红外相机
    PVIC_LEAP_MOTION = '00301'
    # 物理-视觉-Basler红外相机
    PVIC_BASLER_ = '00302'
    # 物理-视觉-七鑫易维aSee穿戴式眼动仪
    PVGC_ASEE_GLASS = '00501'
    # 物理-视觉-SmartEye遥测式眼动仪
    PVGC_SMART_EYE = '00502'
    # 物理-视觉-SmartEye眼动仪快照
    PVGC_SMART_EYE_SNAPSHOT = '00503'
    # 物理-视觉-Tobii EyeTracker 5遥测式眼动仪
    PVGC_TOBII_ET5 = '00504'
    # 物理-听觉-大疆麦克风
    PAMC_DJI = '02101'
    # 物理-听觉-罗德麦克风
    PAMC_RODE_WL2 = '02102'
    # 物理-力触觉-优派触控屏
    PTM_VIEWSONIC_ = '04101'
    # 物理-生理-博睿康DSI-24脑电帽
    PEEG_NEURACLE = '05101'
    # 物理-生理-中科心研手环-GSR
    PWBD_PSYCH_TECH_GSR = '05201'
    # 物理-生理-中科心研手环-PPG
    PWBD_PSYCH_TECH_PPG = '05202'
    # 物理-生理-PHYSIOLAB
    PPHS_PHYSIOLAB = '05301'
    PPQS_TIME = '07011'
    PPQS_GPS = '07012'
    # 仿真-BeamNG-相机
    VBNG_CAMERA = '10001'
    # 仿真-BeamNG-激光雷达
    VBNG_LIDAR = '10101'
    # 仿真-BeamNG-雷达
    VBNG_RADAR = '10102'
    # 仿真-BeamNG-理想雷达
    VBNG_IDEAL_RADAR = '10103'
    # 仿真-BeamNG-超声
    VBNG_ULTRA_SONIC = '10301'
    # 仿真-BeamNG-传感器组
    VBNG_CLASSICAL = '10701'
    # 仿真-BeamNG-GPS
    VBNG_GPS = '10702'
    # 仿真-BeamNG-高级IMU
    VBNG_IMU_ADV = '10712'
    # 仿真-BeamNG-道路
    VBNG_ROAD = '10731'
    # 仿真-BeamNG-导航
    VBNG_NAVIGATION = '10732'


class ESensorChannelType(Enum):
    # 未指定
    IGNORE = '00'
    # 头部姿态
    HEAD_POSE = '01'
    # 上身姿态
    HALF_BODY_POSE = '02'
    # 全身姿态
    BODY_POSE = '03'
    # 眼
    EYES = '04'
    # 脸（身份）
    FACE_ID = '05'
    # 脸（表情）
    FACE_EXPR = '06'
    # 语音
    SPEECH = '07'
    # 手
    HAND = '08'
    # 手指
    FINGER = '09'
    # 手腕
    WRIST = '10'
    # 手臂
    ARM = '11'
    # 脑
    BRAIN = '12'
    # 身体
    BODY = '13'
    # 心
    HEART = '14'
    # 皮肤
    SKIN = '15'
    # 肌肉
    MUSCLE = '16'
    # 情绪
    EMOTION = '17'
    # 左手
    HAND_LEFT = '31'
    # 右手
    HAND_RIGHT = '32'
    # 左手指
    FINGER_LEFT = '33'
    # 右手指
    FINGER_RIGHT = '34'
    # 左手腕
    WRIST_LEFT = '35'
    # 右手腕
    WRIST_RIGHT = '36'
    # 左臂
    ARM_LEFT = '37'
    # 右臂
    ARM_RIGHT = '38'


class ESensorModality(Enum):
    # 未指定
    IGNORE = '00'
    # 图像
    IMAGE = '01'
    # 视频
    VIDEO = '02'
    # 点
    POINT = '03'
    # 点云
    POINT_CLOUD = '04'
    # 轨迹
    TRAJECTORY = '05'
    # 音频
    AUDIO = '06'
    # 文本
    TEXT = '07'
    # 生理
    PHYSIO = '08'
    # 向量
    VEC = '09'


class ESensorDataFormat(Enum):
    # 未指定
    IGNORE = '00'
    # 图像-原始
    RAW = '01'
    # 图像-YUV
    YUV = '02'
    # 图像-JPEG
    JPEG = '03'
    # 图像-PNG
    PNG = '04'
    # 图像-BMP
    BMP = '05'
    # 视频-AVI
    AVI = '11'
    # 声音-PCM
    PCM = '21'
    # 声音-WAV
    WAV = '22'
    # 文本-TXT
    TXT = '31'
    # 文本-JSON
    JSON = '32'
    # 文本-CSV
    CSV = '33'
    # 数据-NPY
    NPY = '41'
    # 数据-NPZ
    NPZ = '42'
    # 数据-H5
    H5 = '43'
    # 数据-EDF
    EDF = '44'


class STag:
    """
    传感器标签编码器，编码形如：`210702001000301`，表示 `2-10702-001-00-03-01`
    """

    @staticmethod
    def from_int(stag: int):
        """
        根据整数编码表示创建 STag 对象
        :param stag: 传感器标签编码，形如：`210702030100001`
        :return: STag 对象
        """
        return STag.from_str(str(stag))

    @staticmethod
    def from_str(stag: str):
        """
        根据字符串编码表示创建 STag 对象
        :param stag: 传感器标签编码，形如：`"210702030100001"`
        :return: STag 对象
        """
        if len(stag) != 15:
            raise ValueError('stag')
        kwargs = dict(sensor_domain=ESensorDomain(stag[0]).name,
                      sensor_type=ESensorType(stag[1:6]).name,
                      sensor_modality=ESensorModality(stag[6:8]).name,
                      sensor_data_format=ESensorDataFormat(stag[8:10]).name,
                      sensor_channel_type=ESensorChannelType(stag[10:12]).name,
                      sensor_channel=int(stag[12:15]))
        return STag(**kwargs)

    def __init__(self, **kwargs):
        """
        初始化
        :param kwargs: 字典，应包含：domain, sensor_type, modality, data_format, channel_type, channel 元素
        """
        self._kwargs = kwargs

    def encode(self) -> int:
        """
        编码为 int 格式
        :return: 传感器标签编码，形如：`210702030100001`
        """
        return int(self.to_str(friendly=False))

    def to_tuple(self) -> tuple:
        """
        转换为元组

        :return: 元组，按顺序为：领域、类型、通道类型、通道号、模态、数据格式
        """
        return self._encode()

    def to_str(self, friendly=True):
        """
        编码为 str 格式
        :return: 传感器标签编码的可读字符串形式，形如：`2-10702-03-01-00-001`
        """
        (sensor_domain, sensor_type, sensor_channel_type,
         sensor_channel, sensor_modality, sensor_data_format) = self._encode()
        fmt = '{d}-{s}-{m}-{df}-{ct}-{c:0>3d}' if friendly else '{d}{s}{m}{df}{ct}{c:0>3d}'
        stag = fmt.format(d=sensor_domain.value, s=sensor_type.value,
                          m=sensor_modality.value, df=sensor_data_format.value,
                          ct=sensor_channel_type.value, c=sensor_channel, )
        return stag

    def _encode(self) -> tuple:
        sensor_channel = int(self._kwargs.get('sensor_channel'))
        if sensor_channel < 0 or sensor_channel > 999:
            raise ValueError('sensor_channel should be between 0 and 999')

        sensor_domain = ESensorDomain[self._kwargs.get('sensor_domain', 'IGNORE').upper()]
        sensor_type = ESensorType[self._kwargs.get('sensor_type', 'IGNORE').upper()]
        sensor_channel_type = ESensorChannelType[self._kwargs.get('sensor_channel_type', 'IGNORE').upper()]
        sensor_modality = ESensorModality[self._kwargs.get('sensor_modality', 'IGNORE').upper()]
        sensor_data_format = ESensorDataFormat[self._kwargs.get('sensor_data_format', 'IGNORE').upper()]

        return (sensor_domain, sensor_type, sensor_channel_type,
                sensor_channel, sensor_modality, sensor_data_format)
